「不就是一個爬蟲加下載器嗎?能跑就好,寫什麼測試?」
這大概是很多人對這類個人專案的第一反應。我自己也是這樣開始的:先求能動,能把 M3U8 片段兜起來變成一份完整內容,就算成功。但這種工具有一個特性,會讓「先求能動」這個策略慢慢撐不住——它依賴的每一層幾乎都不在你控制範圍內:來源站點隨時可能改版面結構、網路請求隨時可能逾時或中斷、片段可能被加密、下載到一半的檔案可能損毀。任何一層出狀況,症狀都長得一樣:程式跑到一半卡住,或者跑完了但輸出的內容是壞的。
這個系列要講的,不是「怎麼寫一個下載器」,而是當你手上的程式滿是不可控的外部依賴時,怎麼讓它變成一個你敢放心修改、放心重構的東西——而不是一個「能跑就不要動它」的黑盒子。
這個系列的素材,來自一個真實運作、我自己在用的個人專案:一支處理 HLS(HTTP Live Streaming)分段式串流內容的下載工具。這個系列會聚焦在它面對的一種常見網路架構模式——內容被拆成一份份小片段透過 HTTP 提供、片段清單另外用一份索引檔描述、部分片段還做了加密處理。這種「索引檔 + 分段內容 + 可能加密」的模式不是影音串流獨有:CDN 內容分發、大型檔案的分片斷點續傳、甚至裝置韌體的 OTA 更新,都有類似的設計思路,只是索引格式、加密方式各自不同。
用 HLS 協定為例,處理流程拆開來看是這樣:
聽起來是很直觀的四個步驟,但每一步都踩過至少一次坑:索引清單的巢狀結構讓「以為抓到片段清單」結果只抓到另一層索引、加密片段解密出來是亂碼、下載到一半斷線後重新執行卻把已經下載好的片段又重抓一次、合併後發現片段之間規格對不上。
這些坑不是靠「更小心寫程式碼」解決的——是靠先確認清楚每一層的行為邊界,再用測試把邊界釘住,才不會每次改動都提心吊膽。
這個系列會反覆回到同一個問題:**遇到不在你控制範圍內的依賴,要怎麼讓程式碼還是可以被測試?**具體會處理三種:
不可控不代表不能測,關鍵是找到每種依賴各自的替身策略。 這句話會是這個系列每一篇文章反覆回收的主題句。
四大部,從外到內、從架構到測試:
回想你手上有沒有一個「能跑就好,沒人敢動」的小工具?它讓你不敢動手改的原因,是邏輯太複雜,還是依賴太多、不知道改了會不會炸?
明天從第一部開始:不同來源站點的頁面結構天差地遠,這一層抽象要怎麼切,才不會變成一堆散落各處的 if-else。